#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "test.h"

static void write_data(int fd, byte val)
{
    int ret;
    ioctl(fd, DATA);
    ret = write(fd, &val, sizeof(byte));
    if (ret != sizeof(byte)) 
        printf("write data error\n");
}

static void write_cmd(int fd, byte val)
{
    int ret;
    ioctl(fd, CMD);
    ret = write(fd, &val, sizeof(byte));
    if (ret != sizeof(byte)) 
        printf("write cmd error\n");
}

static inline void Set_Display_On_Off(int fd, byte d)
{
    write_cmd(fd, 0xAE | d);
}
static inline void Set_Display_Clock(int fd, byte d)
{
    write_cmd(fd, 0xD5);
    write_cmd(fd, d);
}
static inline void Set_Multiplex_Ratio(int fd, byte d)
{
    write_cmd(fd, 0xA8);
    write_cmd(fd, d);
}
static inline void Set_Display_Offset(int fd, byte d)
{
    write_cmd(fd, 0xC0 | d);
}
static inline void SetStartLine(int fd, byte d)
{
    write_cmd(fd, 0x40 | d);
}
static inline void Set_Charge_Pump(int fd, byte d)
{
    write_cmd(fd, 0x8D);
    write_cmd(fd, 0x10 | d);
}
static inline void SetAddressingMode(int fd, byte d)
{
    write_cmd(fd, 0x20);
    write_cmd(fd, d);
}
static inline void Set_Segment_Remap(int fd, byte d)
{
    write_cmd(fd, 0xA0 | d);
}
static inline void Set_Common_Remap(int fd, byte d)
{
    write_cmd(fd, 0xC0 | d);
}
static inline void Set_Common_Config(int fd, byte d)
{
    write_cmd(fd, 0xDA);
    write_cmd(fd, 0x02 | d);
}
static inline void SetContrastControl(int fd, byte d)
{
    write_cmd(fd, 0x81);
    write_cmd(fd, d);
}
static inline void Set_Precharge_Period(int fd, byte d)
{
    write_cmd(fd, 0xD9);
    write_cmd(fd, d);
}
static inline void Set_VCOMH(int fd, byte d)
{
    write_cmd(fd, 0xDB);
    write_cmd(fd, d);
}
static inline void Set_Entire_Display(int fd, byte d)
{
    write_cmd(fd, 0xA4 | d);
}
static inline void Set_Inverse_Display(int fd, byte d)
{
    write_cmd(fd, 0xA6 | d);
}
static inline void LCD_Fill(int fd, byte d)
{
	byte y,x;	
	for(y = 0; y < 8; y++)
	{
		write_cmd(fd, 0xB0 + y);
		write_cmd(fd, 0x01);
		write_cmd(fd, 0x10);
		for(x = 0; x < X_WIDTH; x++)
			write_data(fd, d);
	}
}
static inline void LCD_Set_Pos(int fd, byte x, byte y)
{
    write_cmd(fd, 0xB0 + y);
    write_cmd(fd, ((x & 0xF0) >> 4) | 0x10 );
    write_cmd(fd, (x & 0x0F) | 0x01 ); 
}
static void oled_init(int fd)
{
    Set_Display_On_Off  (fd, 0x00);		  // Display Off (0x00/0x01)
    Set_Display_Clock   (fd, 0x80);		  // Set Clock as 100 Frames/Sec
    Set_Multiplex_Ratio (fd, 0x3F);		  // 1/64 Duty (0x0F~0x3F)
    Set_Display_Offset  (fd, 0x00);		  // Shift Mapping RAM Counter (0x00~0x3F)
    SetStartLine        (fd, 0x00);	      // Set Mapping RAM Display Start Line (0x00~0x3F)
    Set_Charge_Pump     (fd, 0x04);		  // Enable Embedded DC/DC Converter (0x00/0x04)
    SetAddressingMode   (fd, 0x02);		  // Set Page Addressing Mode (0x00/0x01/0x02)
    Set_Segment_Remap   (fd, 0x01);		  // Set SEG/Column Mapping     0x00左右反置 0x01正常
    Set_Common_Remap    (fd, 0x08);		  // Set COM/Row Scan Direction 0x00上下反置 0x08正常
    Set_Common_Config   (fd, 0x10);		  // Set Sequential Configuration (0x00/0x10)
    SetContrastControl  (fd, 0xCF);       // Set SEG Output Current
    Set_Precharge_Period(fd, 0xF1);	      // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    Set_VCOMH           (fd, 0x40);		  // Set VCOM Deselect Level
    Set_Entire_Display  (fd, 0x00);		  // Disable Entire Display On (0x00/0x01)
    Set_Inverse_Display (fd, 0x00);		  // Disable Inverse Display On (0x00/0x01)  
    Set_Display_On_Off  (fd, 0x01);		  // Display On (0x00/0x01)
    LCD_Fill            (fd, 0x00);       //初始清屏
	LCD_Set_Pos         (fd, 0, 0); 
}
//其中x1、x2的范围0～127，y1，y2的范围0～63
void LCD_Rectangle(int fd, byte x1, byte y1, byte x2, byte y2, byte gif)
{
	byte n; 	
	LCD_Set_Pos(fd, x1, y1 >> 3);
	for(n = x1; n <= x2; n++) {
		write_data(fd, 0x01 << (y1 % 8)); 			
		if(gif == 1)
            usleep(50 * 1000); 	
	}  
	LCD_Set_Pos(fd, x1, y2 >> 3);
    
    for(n = x1; n <= x2; n++) {
		write_data(fd, 0x01 << (y2 % 8)); 			
		if(gif == 1)
            usleep(50 * 1000); 	 	
	}
} 
//显示的位置（x,y），y为页范围0～7
void LCD_P6x8Str(int fd, byte x, byte y, byte ch[])
{
    byte c = 0, i = 0, j = 0;      
    while (ch[j] != '\0') {    
        c = ch[j] - 32;
        if(x > 126) {
            x = 0;
            y++;
        }
        LCD_Set_Pos(fd, x, y);    
  	    for(i = 0; i < 6; i++)     
  	        write_data(fd, F6x8[c][i]);  
  	    x += 6;
  	    j++;
    }
}
// 显示的位置（x,y），y为页范围0～7
void LCD_P8x16Str(int fd, byte x, byte y, byte ch[])
{
    byte c = 0, i = 0, j = 0;
    while (ch[j] != '\0') {    
        c = ch[j] - 32;
        if( x > 120) {
            x = 0;
            y++;
        }
        LCD_Set_Pos(fd, x, y);    
  	    for(i = 0; i < 8; i++)     
  	        write_data(fd, F8X16[c * 16 + i]);
    	LCD_Set_Pos(fd, x, y + 1);    
  	    for(i = 0; i < 8; i++)     
  	        write_data(fd, F8X16[c * 16 + i + 8]);  
        x += 8;
  	    j++;
    }
}
int main(int argc, char* argv[])
{
    int fd;
    char str1[] = "imx6ull spi";
    char str2[] = "oled driver";
    fd = open(PATH, O_RDWR);
    if (fd < 0) {
        printf("open %s error\n", PATH);
        return ERROR;
    }
    oled_init(fd);
    //LCD_Rectangle(fd, 10, 10, 120, 60, 1);
    //LCD_P6x8Str(fd, 20, 0, str1);
    //LCD_P6x8Str(fd, 20, 4, str2);
    LCD_P8x16Str(fd, 20, 1, str1);
    LCD_P8x16Str(fd, 20, 3, str2);
    close(fd);
    return OK;
}